package bg.cinema_system.beans;

import java.io.IOException;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedProperty;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.Flash;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.primefaces.context.RequestContext;

/**
 * @author user
 *
 */
public class BaseBean implements Serializable {

	private static final long serialVersionUID = 1L;

	public FacesContext getFacesContext() {
		return FacesContext.getCurrentInstance();
	}

	public javax.faces.application.Application getApplication() {
		return getFacesContext().getApplication();
	}

	public HttpSession getSession() {
		HttpSession session = (HttpSession) getFacesContext().getExternalContext().getSession(false);
		return session;
	}

	/**
	 * resetUIComponent - after validation error the UIcomponents keep the old values.
	 * We need to reset them in order to reload normally the new data from the
	 * bean.
	 * 
	 * @param componentName
	 *            String
	 */
	public void resetUIComponent(String componentName) {
		resetUIComponent(findComponentInRoot(componentName));
	}

	/**
	 * resetUIComponent - after validation error the UIcomponents keep the old values.
	 * We need to reset them in order to reload normally the new data from the
	 * bean.
	 * 
	 * @param component
	 *            UIComponent
	 */
	public void resetUIComponent(UIComponent component) {
		if (component != null) {
			for (UIComponent uic : component.getChildren()) {
				if (uic instanceof EditableValueHolder) {
					EditableValueHolder evh = (EditableValueHolder) uic;
					evh.resetValue();
				}

				resetUIComponent(uic);
			}
		}
	}

	/**
	 * Get an HTTP Request attribute.
	 * 
	 * @param name
	 *            attribute name
	 * @return attribute value
	 */
	public Object getFromRequest(String name) {
		return getFacesContext().getExternalContext().getRequestMap().get(name);
	}

	/**
	 * Set an HTTP Request attribute.
	 * 
	 * @param name
	 *            attribute name
	 * @param value
	 *            attribute value
	 */
	public void setToRequest(String name, Object value) {
		getFacesContext().getExternalContext().getRequestMap().put(name, value);
	}

	/**
	 * Convenience method for getting Request parameters.
	 * 
	 * @param key
	 *            object key
	 * @return session object for key
	 */
	public String getFromRequestParameters(String key) {
		FacesContext ctx = getFacesContext();
		Map<String, String> sessionState = ctx.getExternalContext().getRequestParameterMap();
		return sessionState.get(key);
	}

	/**
	 * Convenience method for setting Session attributes.
	 * 
	 * @param key
	 *            object key
	 * @param object
	 *            value to store
	 */

	public void setToSession(String key, Object object) {
		FacesContext ctx = getFacesContext();
		((HttpSession)ctx.getExternalContext().getSession(false)).setAttribute(key, object);
	}
	
	/**
	 * Convenience method for getting Session attributes.
	 * 
	 * @param key
	 *            object key
	 * @return session object for key
	 */
	public Object getFromSession(String key) {
		FacesContext ctx = getFacesContext();
		Map<String, Object> sessionState = ctx.getExternalContext().getSessionMap();
		return sessionState.get(key);
	}
	
	/**
	 * Convenience method for removing Session attributes
	 * 
	 * @param key
	 *            the attribute's key
	 */
	public void removeFromSession(String key) {
		FacesContext ctx = getFacesContext();
		((HttpSession)ctx.getExternalContext().getSession(false)).removeAttribute(key);
	}

	/**
	 * Add JSF error message for a specific attribute.
	 * 
	 * @param attrName
	 *            name of attribute
	 * @param msg
	 *            error message string
	 */
	public void addFacesMessage(String attrName, String msg) {
		// TODO: Need a way to associate attribute specific messages
		// with the UIComponent's Id! For now, just using the view id.
		// TO SEE: make this use the internal getMessageFromBundle?
		// TO SEE: possibility to add FacesErrorMessage &
		// FacesInformationMessage
		// (use FacesMessage(FacesMessage.SEVERITY_ERROR, msg, "");)
		FacesContext ctx = getFacesContext();
		FacesMessage fm = new FacesMessage(msg);
		ctx.addMessage(attrName, fm);
	}

	/**
	 * Locate an UIComponent in view root with its component id. Use a recursive
	 * way to achieve this. Taken from
	 * http://www.jroller.com/page/mert?entry=how_to_find_a_uicomponent
	 * 
	 * @param id
	 *            UIComponent id
	 * @return UIComponent object
	 */
	public UIComponent findComponentInRoot(String id) {
		UIComponent component = null;
		FacesContext facesContext = FacesContext.getCurrentInstance();
		if (facesContext != null) {
			UIComponent root = facesContext.getViewRoot();
			component = findComponent(root, id);
		}
		return component;
	}

	/**
	 * Locate an UIComponent from its root component. Taken from
	 * http://www.jroller.com/page/mert?entry=how_to_find_a_uicomponent
	 * 
	 * @param base
	 *            root Component (parent)
	 * @param id
	 *            UIComponent id
	 * @return UIComponent object
	 */
	public UIComponent findComponent(UIComponent base, String id) {
		if (id.equals(base.getId()))
			return base;

		UIComponent children = null;
		UIComponent result = null;
		Iterator<UIComponent> childrens = base.getFacetsAndChildren();
		while (childrens.hasNext() && (result == null)) {
			children = (UIComponent) childrens.next();
			if (id.equals(children.getId())) {
				result = children;
				break;
			}
			result = findComponent(children, id);
			if (result != null) {
				break;
			}
		}
		return result;
	}

	/**
	 * Get view id of the view root.
	 * 
	 * @return view id of the view root
	 */
	public String getRootViewId() {
		return getFacesContext().getViewRoot().getViewId();
	}

	/**
	 * Get component id of the view root.
	 * 
	 * @return component id of the view root
	 */
	public String getRootViewComponentId() {
		return getFacesContext().getViewRoot().getId();
	}

	/**
	 * Method to create a redirect URL. The assumption is that the JSF servlet
	 * mapping is "faces", which is the default
	 * 
	 * @param view
	 *            the JSP or JSPX page to redirect to
	 * @return a URL to redirect to
	 */
	public String getPageURL(String view) {
		FacesContext facesContext = getFacesContext();
		ExternalContext externalContext = facesContext.getExternalContext();
		String url = ((HttpServletRequest) externalContext.getRequest()).getRequestURL().toString();
		StringBuffer newUrlBuffer = new StringBuffer();
		newUrlBuffer.append(url.substring(0, url.lastIndexOf("faces/")));
		newUrlBuffer.append("faces");
		String targetPageUrl = view.startsWith("/") ? view : "/" + view;
		newUrlBuffer.append(targetPageUrl);
		return newUrlBuffer.toString();
	}

	private static final String NO_RESOURCE_FOUND = "Missing resource: ";

	/**
	 * getBundle - Internal method to pull out the correct local message bundle
	 * 
	 * @return
	 */
	private ResourceBundle getBundle() {
		// FacesContext ctx = getFacesContext();
		// UIViewRoot uiRoot = ctx.getViewRoot();
		// Locale locale = uiRoot.getLocale();
		// ClassLoader ldr = Thread.currentThread().getContextClassLoader();
		// return ResourceBundle.getBundle("MessageBundle", locale, ldr);
		return ResourceBundle.getBundle("MessageBundle");
	}

	/**
	 * Pulls a String resource from the property bundle that is defined under
	 * the application &lt;message-bundle&gt; element in the faces config.
	 * Respects Locale
	 * 
	 * @param key
	 *            string message key
	 * @return Resource value or placeholder error String
	 */
	public String getStringFromBundle(String key) {
		ResourceBundle bundle = getBundle();
		return getStringSafely(bundle, key, null);
	}

	/**
	 * Convenience method to construct a <code>FacesMesssage</code> from a
	 * defined error key and severity This assumes that the error keys follow
	 * the convention of using <b>_detail</b> for the detailed part of the
	 * message, otherwise the main message is returned for the detail as well.
	 * 
	 * @param key
	 *            for the error message in the resource bundle
	 * @param severity
	 *            severity of message
	 * @return Faces Message object
	 */
	public FacesMessage getMessageFromBundle(String key, FacesMessage.Severity severity) { // TODO
		ResourceBundle bundle = getBundle();
		String summary = getStringSafely(bundle, key, null);
		String detail = getStringSafely(bundle, key + "_detail", summary);
		FacesMessage message = new FacesMessage(summary, detail);
		message.setSeverity(severity);
		return message;
	}

	/*
	 * Internal method to proxy for resource keys that don't exist
	 */
	private String getStringSafely(ResourceBundle bundle, String key, String defaultValue) {
		String resource = null;
		try {
			resource = bundle.getString(key);
		} catch (MissingResourceException mrex) {
			if (defaultValue != null) {
				resource = defaultValue;
			} else {
				resource = NO_RESOURCE_FOUND + key;
			}
		}
		return resource;
	}

	/**
	 * This method returns the current instance of {@link RequestContext}
	 * 
	 * @return the current instance of {@link RequestContext}
	 */
	public RequestContext getRequestContext() {
		return RequestContext.getCurrentInstance();
	}
	
	/**
	 * This method show the exception dialog
	 * 
	 * @param message - information about the exception for the users, that will be in the dialog
	 */
	public void showExceptionDialog(String message){
		Flash flash = getFacesContext().getExternalContext().getFlash();
		flash.put("exceptiondDialogMsg", message);
		getRequestContext().execute("newExceptionDialog.show()");
		getRequestContext().update("exceptionFormGlobal");
	}
	
	public void redirectToErrorPage(String message) {
		Flash flash =getFacesContext().getExternalContext().getFlash();
		flash.put("errorDetails", message);
		try {
			getFacesContext().getExternalContext().redirect("error.xhtml");
		} catch (IOException e) {
			
		}
	}
	
	public String getAppRootURL() {
		HttpServletRequest request = (HttpServletRequest)getFacesContext().getExternalContext().getRequest();
		String requestURL = request.getRequestURL().toString();
		String rootURL = requestURL.substring(0, requestURL.lastIndexOf('/'));
		return rootURL;
	}
	
}
